package com.activiti.flow.service.Impl;

import com.activiti.basic.exception.ApprovalException;
import com.activiti.flow.model.entity.RejectTask;
import com.activiti.flow.model.vo.ProcessDefinitionVO;
import com.activiti.flow.model.vo.ProcessInstanceVO;
import com.activiti.flow.model.vo.RejectTaskVO;
import com.activiti.flow.service.ProcessInstanceService;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.persistence.entity.HistoricActivityInstanceEntity;
import org.activiti.engine.impl.persistence.entity.HistoricActivityInstanceEntityManager;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author pisory
 * @version 1.0
 * @date 2020/4/6
 */
@Service
@Slf4j
public class ProcessInstanceServiceImpl implements ProcessInstanceService {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ManagementService managementService;

    @Autowired
    private RepositoryService repositoryService;

    private static final String SKIP_EXPRESSION = "_ACTIVITI_SKIP_EXPRESSION_ENABLED";

    @Override
    public ProcessInstance startProcessByKey(ProcessDefinitionVO vo) throws Exception {
        Map<String, Object> variables = new HashMap<String, Object>();

        // 用户1
        List<String> teamLeaderList = new ArrayList<>();
        variables.put("Leave_User1_Node", "组长李四");

        // 用户2
        variables.put("Leave_User2_Node", "部门领导赵五");

        // 查询是否有对应发布的流程

        // 启动流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(vo.getProcessDefinitionKey(), variables);
        return processInstance;
    }

    @Override
    public int pass(ProcessInstanceVO vo) throws ApprovalException {
        // 查询任务状态
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(vo.getProcessInstanceId()).list();
        if (CollectionUtils.isEmpty(taskList)) {
            throw new ApprovalException("没有此任务，请检查任务");
        }
        log.info("同意通过审批任务，审批任务={}", taskList.get(0));
        if (vo.isNeedContinuityApproval()) {
            recursionPass(vo, taskList);
        } else {
            pass(vo, taskList);
        }
        return 1;
    }

    @Override
    public void reject(RejectTaskVO vo) throws ApprovalException {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(vo.getProcessInstanceId()).list();
        if (CollectionUtils.isEmpty(taskList)) {
            throw new ApprovalException("没有此任务，请检查任务");
        }
        doReject(taskList, vo);
    }

    @Override
    public void cancel(ProcessInstanceVO vo) throws ApprovalException {
        List<Task> taskList = taskService.createTaskQuery().processInstanceId(vo.getProcessInstanceId()).list();

        // 直接删除流程，不删除流程历史数据
        runtimeService.deleteProcessInstance(vo.getProcessInstanceId(), vo.getApproveComment());

        // 删除审批任务
        for (Task task : taskList) {
            taskService.deleteTask(task.getId());
        }
    }

    @Override
    public void entrust(ProcessInstanceVO vo) throws ApprovalException {

    }

    /**
     * 单个节点审批
     *
     * @param vo
     * @param taskList
     */
    private void pass(ProcessInstanceVO vo, List<Task> taskList) {
        doPass(vo, taskList);
    }

    /**
     * 连续审批，多个节点相同审批人
     *
     * @param vo
     * @param taskList
     */
    private void recursionPass(ProcessInstanceVO vo, List<Task> taskList) {
        // 查询当前节点审批人
        String currentAuditor = getCurrentAuditor(vo);

        // 当前节点审批 == 登录用户

        // 通过
        doPass(vo, taskList);

        // 循环调用审批通过
        pass(vo);
    }

    /**
     * 审批
     *
     * @param vo
     * @param taskList
     */
    private void doPass(ProcessInstanceVO vo, List<Task> taskList) {
        String taskId = taskList.get(0).getId();
        log.info("审批通过任务：taskId = " + taskId);
        taskService.claim(taskId, vo.getUserName());
        taskService.complete(taskId);
    }

    private String getCurrentAuditor(ProcessInstanceVO vo) {
        // 查询当前审批节点用户

        return null;
    }

    private void doReject(List<Task> taskList, RejectTaskVO vo) {
        String destinationTaskId = null;
        String nodeName = null;
        String currentTaskId = taskList.get(0).getId();
        // ------------------------下面这段需要优化------------------------
        ProcessDefinitionEntity def = (ProcessDefinitionEntity) ((RepositoryServiceImpl)repositoryService).getDeployedProcessDefinition(taskList.get(0).getProcessDefinitionId());
        List<ActivityImpl> activitiList = def.getActivities();
        for(ActivityImpl act:activitiList){
            if(act.getId().equals(vo.getNodeCode())){
                nodeName = (String) act.getProperty("name");
                break;
            }
        }

        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().processInstanceId(vo.getProcessInstanceId()).list();
        for (HistoricActivityInstance historicActivityInstance : list) {
            if(historicActivityInstance.getActivityName().equals(nodeName)) {
                destinationTaskId = historicActivityInstance.getId();
                break;
            }
        }
        // ------------------------上面这段需要优化------------------------

        // 目标任务实例 historicDestinationTaskInstance 带流程变量，任务变量
        HistoricTaskInstance historicDestinationTaskInstance = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(vo.getProcessInstanceId())
                .includeProcessVariables()
                .includeTaskLocalVariables()
                .singleResult();

        // TODO 正在执行的任务实例 historicCurrentTaskInstance 带流程变量，任务变量，不应该使用历史的，应该使用运行时的任务
        HistoricTaskInstance historicCurrentTaskInstance = historyService
                .createHistoricTaskInstanceQuery()
                .taskId(currentTaskId)
                .includeProcessVariables()
                .includeTaskLocalVariables()
                .singleResult();

        // 流程定义ID
        String processDefinitionId = historicCurrentTaskInstance.getProcessDefinitionId();
        // 流程实例ID
        String processInstanceId = historicCurrentTaskInstance.getProcessInstanceId();
        // 流程定义实体
        ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);

        // 根据任务创建时间正序排序获取历史任务实例集合 historicTaskInstanceList 含流程变量，任务变量
        List<HistoricTaskInstance> historicTaskInstanceList = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .includeProcessVariables()
                .includeTaskLocalVariables()
                .orderByTaskCreateTime()
                .asc()
                .list();

        // 历史活动节点实例集合 historicActivityInstanceList
        List<HistoricActivityInstance> historicActivityInstanceList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceStartTime()
                .asc()
                .list();

        // 获取目标任务的节点信息
        ActivityImpl destinationActivity = processDefinition.findActivity(historicDestinationTaskInstance.getTaskDefinitionKey());
        // 定义一个历史任务集合，完成任务后任务删除此集合中的任务
        List<HistoricTaskInstance> deleteHistoricTaskInstanceList = new ArrayList<>();
        // 定义一个历史活动节点集合，完成任务后要添加的历史活动节点集合
        List<HistoricActivityInstanceEntity> insertHistoricTaskActivityInstanceList = new ArrayList<>();

        // 目标任务编号
        Integer destinationTaskInstanceId = Integer.valueOf(destinationTaskId);

        // 有序
        for (HistoricTaskInstance historicTaskInstance : historicTaskInstanceList) {
            Integer historicTaskInstanceId = Integer.valueOf(historicTaskInstance.getId());
            if (destinationTaskInstanceId <= historicTaskInstanceId) {
                deleteHistoricTaskInstanceList.add(historicTaskInstance);
            }
        }
        // 有序
        for (int i = 0; i < historicActivityInstanceList.size() - 1; i++) {
            HistoricActivityInstance historicActivityInstance = historicActivityInstanceList.get(i);
            // 历史活动节点的任务编号
            Integer historicActivityInstanceTaskId;
            String taskId = historicActivityInstance.getTaskId();
            if (taskId != null) {
                historicActivityInstanceTaskId = Integer.valueOf(taskId);
                if (historicActivityInstanceTaskId <= destinationTaskInstanceId) {
                    insertHistoricTaskActivityInstanceList.add((HistoricActivityInstanceEntity) historicActivityInstance);
                }
            } else {
                insertHistoricTaskActivityInstanceList.add((HistoricActivityInstanceEntity) historicActivityInstance);
            }
        }

        // 获取流程定义的节点信息
        List<ActivityImpl> processDefinitionActivities = processDefinition.getActivities();
        // 用于保存正在执行的任务节点信息
        ActivityImpl currentActivity = null;
        // 用于保存原来的任务节点的出口信息
        PvmTransition pvmTransition = null;
        // 保存原来的流程节点出口信息
        for (ActivityImpl activity : processDefinitionActivities) {
            if (historicCurrentTaskInstance.getTaskDefinitionKey().equals(activity.getId())) {
                currentActivity = activity;
                // 备份
                pvmTransition = activity.getOutgoingTransitions().get(0);
                // 清空当前任务节点的出口信息
                activity.getOutgoingTransitions().clear();
            }
        }

        // 执行流程转向
        managementService.executeCommand(new RejectTask(historicDestinationTaskInstance, historicCurrentTaskInstance, destinationActivity));
        // 获取正在执行的任务的流程变量
        Map<String, Object> taskLocalVariables = historicCurrentTaskInstance.getTaskLocalVariables();
        // 获取目标任务的流程变量，修改任务不自动跳过，要求审批
        Map<String, Object> processVariables = historicDestinationTaskInstance.getProcessVariables();
        processVariables.put(SKIP_EXPRESSION, false);
        taskLocalVariables.put(SKIP_EXPRESSION, false);
        // 完成当前任务，任务走向目标任务
        taskService.setVariablesLocal(currentTaskId, taskLocalVariables);
        taskService.complete(currentTaskId, processVariables);
        if (currentActivity != null) {
            // 清空临时转向信息
            currentActivity.getOutgoingTransitions().clear();
        }
        if (currentActivity != null) {
            // 恢复原来的走向
            currentActivity.getOutgoingTransitions().add(pvmTransition);
        }
        // 删除历史任务
        for (HistoricTaskInstance historicTaskInstance : deleteHistoricTaskInstanceList) {
            historyService.deleteHistoricTaskInstance(historicTaskInstance.getId());
        }

        // 删除活动节点
        managementService.executeCommand(
                (Command<List<HistoricActivityInstanceEntity>>) commandContext -> {
                    HistoricActivityInstanceEntityManager historicActivityInstanceEntityManager =
                            commandContext.getHistoricActivityInstanceEntityManager();
                    // 删除所有的历史活动节点
                    historicActivityInstanceEntityManager
                            .deleteHistoricActivityInstancesByProcessInstanceId(processInstanceId);
                    // 提交到数据库
                    commandContext.getDbSqlSession().flush();
                    // 添加历史活动节点的
                    for (HistoricActivityInstanceEntity historicActivityInstance : insertHistoricTaskActivityInstanceList) {
                        historicActivityInstanceEntityManager.insertHistoricActivityInstance(historicActivityInstance);
                    }
                    // 提交到数据库
                    commandContext.getDbSqlSession().flush();
                    return null;
                }
        );
    }
}
